package org.eclipse.wst.server.preview.openejb.jetty.loader;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

import org.apache.openejb.loader.ClassPath;
import org.apache.openejb.loader.FileUtils;
import org.apache.openejb.loader.SystemInstance;

/**Embedder : This class is responsible to add the libs 
 * dir required by the container to the OpenEJB system 
 * classpath. After that, the OpenEJB http server 
 * instantiation process is triggered calling the 
 * OpenEJBLoader.init() method. 
 * 
 * @author Marcelo de Oliveira Dias
 *
 */
public class Embedder {

	private final String className;
    private Class loaderClass;
    
    /**Class constructor
     * 
     * @param className - the class which initiates OpenEJB.
     */
    public Embedder(String className) {
        this.className = className;
    }

    /**Loads the class which initiates the OpenEJB container.
     * 
     * @return the class loaded.
     * @throws Exception
     */
    public Class load() throws Exception {
        if (loaderClass == null) {
        	//Get the classLoader to be used to load the OpenEJB dependencies.
            ClassPath classPath = SystemInstance.get().getClassPath();
            ClassLoader classLoader = classPath.getClassLoader();
         
            try {
            	//Load the class used to embed the OpenEJB container inside the Jetty.
            	loaderClass = forcefulLoad(classPath, classLoader);
            } catch (Exception e) {
                System.out.println("Unable to loade the class!");
            }
        }
        return loaderClass;
    }

    /**Initiates the OpenEJB container.
     * 
     * @param properties to be used to configure the OpenEJB container.
     * @return
     * @throws Exception
     */
    public Object init(Properties properties) throws Exception {
    	
    	//load the OpenEJBLoader class.
        Class loaderClass = load();

        try {
        	// get the init method
            Method init = loaderClass.getMethod("init", Properties.class);
            
            // create the instance
            Object instance = loaderClass.newInstance();

            // invoke init method to initiates the OpenEJB container.
            Object value = init.invoke(instance, properties);
            return value;
        } catch (NoSuchMethodException e) {
            throw (IllegalStateException) new IllegalStateException("Signatures for Loader are no longer correct.").initCause(e);
        } catch (InvocationTargetException e) {
            Throwable cause = e.getCause();
            if (cause instanceof Error) {
                throw (Error) cause;
            } else {
                throw (Exception) cause;
            }
        }
    }

    /**Load the class responsible to embed OpenEJB into JettyServer.
     * And put the libs dir of the openejb.war to the webapp context that holds OpenEJB container. 
     * 
     * @param classPath
     * @param classLoader
     * @return
     * @throws Exception
     */
    private Class forcefulLoad(ClassPath classPath, ClassLoader classLoader) throws Exception {
        try {
            File libsDir;

            //get the libs dir of the openejb.war deployed by JettyServer.
            String libsPath = SystemInstance.get().getProperty("openejb.libs");
            if (libsPath != null){
            	libsDir = new File(libsPath);
            } else {
                
                FileUtils home = SystemInstance.get().getHome();
                libsDir = home.getDirectory("lib");
            }
            
            //add lib dir of the openejb.war to the webapp context classpath that holds OpenEJB container.
            classPath.addJarsToPath(libsDir);
        } catch (Exception e2) {
            throw new Exception("Could not load OpenEJB libraries. Exception: " + e2.getClass().getName() + " " + e2.getMessage());
        }
        try {
        	//load the OpenEJBLoader
            return classLoader.loadClass(className);
        } catch (Exception e2) {
            throw new Exception("Could not load class '"+className+"' after embedding libraries. Exception: " + e2.getClass().getName() + " " + e2.getMessage());
        }
    }
}
